package edu.jhu.cs.oose.scheduleit.backingbeans;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.faces.context.FacesContext;

import edu.jhu.cs.oose.scheduleit.services.EventService;
import edu.jhu.cs.oose.scheduleit.services.ScheduleService;
import edu.jhu.cs.oose.scheduleit.services.impl.DataBaseException;
import edu.jhu.cs.oose.scheduleit.util.Attribute;
import edu.jhu.cs.oose.scheduleit.util.DateWrapper;
import edu.jhu.cs.oose.scheduleit.util.EventAttributeKind;
import edu.jhu.cs.oose.scheduleit.util.EventType;
import edu.jhu.cs.oose.scheduleit.util.ScheduleAttributeKind;
import edu.jhu.cs.oose.scheduleit.util.ScheduleType;
import edu.jhu.cs.oose.scheduleit.vo.Event;
import edu.jhu.cs.oose.scheduleit.vo.Schedule;
import edu.jhu.cs.oose.scheduleit.vo.User;
import edu.jhu.cs.oose.scheduleit.vo.UserInfo;

/**
 * the backing bean used to deal with all functions related to 'Schedule' feature.
 * @author oose_group2
 *
 */
public class ScheduleBackingBean{

	/**
	 * current login user
	 */
	private User user;
	/**
	 * track the current viewing schedule
	 */
	private Schedule selectSchedule;
	/**
	 * the schedule list of a user
	 */
	private List<Schedule> scheduleList;
	/**
	 * scheduleService reference.
	 */
	private ScheduleService scheduleService;
	/**
	 * eventService reference.
	 */
	private EventService eventService;
	/**
	 * save the topic attribute when creating new schedule.
	 */
	private String topic;
	/**
	 * save the tag attribute when creating new schedule.
	 */
	private String tags;
	/**
	 * save the editable attribute when creating new schedule.
	 */
	private boolean editable;
	/**
	 * save the public_access attribute when creating new schedule.
	 */
	private boolean public_access;
	/**
	 * save the serialize event for the AjaxServlet.
	 */
	private String initXML;
	/**
	 * save the current copied event id for the AjaxServlet.
	 */
	private int copiedEventId;
	/**
	 * constructor of ScheduleBackingBean.
	 */
	public ScheduleBackingBean() {
		FacesContext context = FacesContext.getCurrentInstance();
		UserBackingBean ubb = (UserBackingBean)context.getExternalContext().getSessionMap().get("userBackingBean");
		this.user = ubb.getLogin_user();
	}
	/**
	 * getter of scheduleList
	 * @return
	 */
	public List<Schedule> getScheduleList() {
		try {
			this.scheduleList = this.scheduleService.getSchedule(this.user, ScheduleType.ALL, 0, 0);
		} catch (DataBaseException e) {
			e.printStackTrace();
		}
		return scheduleList;
	}
	/**
	 * getter of user
	 * @return
	 */
	public User getUser() {
		return user;
	}
	/**
	 * getter of copiedEventId
	 * @return
	 */
	public int getCopiedEventId() {
		return copiedEventId;
	}
	/**
	 * setter of copiedEventId
	 * @param copiedEventId
	 */
	public void setCopiedEventId(int copiedEventId) {
		this.copiedEventId = copiedEventId;
	}
	/**
	 * getter of selectSchedule
	 * @return
	 */
	public Schedule getSelectSchedule() {
		return selectSchedule;
	}
	/**
	 * getter of initXML
	 * @return
	 */
	public String getInitXML() {
		return initXML;
	}
	/**
	 * getter of topic
	 * @return
	 */
	public String getTopic() {
		return topic;
	}
	/**
	 * setter of topic
	 * @param topic
	 */
	public void setTopic(String topic) {
		this.topic = topic;
	}
	/**
	 * getter of tags
	 * @return
	 */
	public String getTags() {
		return tags;
	}
	/**
	 * setter of tags
	 * @param tags
	 */
	public void setTags(String tags) {
		this.tags = tags;
	}
	/**
	 * getter of editable
	 * @return
	 */
	public boolean isEditable() {
		return editable;
	}
	/**
	 * setter of editable
	 * @param editable
	 */
	public void setEditable(boolean editable) {
		this.editable = editable;
	}
	/**
	 * getter of public_access.
	 * @return
	 */
	public boolean isPublic_access() {
		return public_access;
	}
	/**
	 * setter of public_Access
	 * @param publicAccess
	 */
	public void setPublic_access(boolean publicAccess) {
		public_access = publicAccess;
	}
	/**
	 * getter of eventService
	 * @return
	 */
	public EventService getEventService() {
		return eventService;
	}
	/**
	 * setter of eventService
	 * @param eventService
	 */
	public void setEventService(EventService eventService) {
		this.eventService = eventService;
	}
	/**
	 * getter of scheduleService
	 * @return
	 */
	public ScheduleService getScheduleService() {
		return scheduleService;
	}
	/**
	 * setter of scheduleService.
	 * @param scheduleService
	 */
	public void setScheduleService(ScheduleService scheduleService) {
		this.scheduleService = scheduleService;
	}
	/**
	 * method to respond to view schedule command
	 * @return JSF direction string
	 */
	public String displaySchedule(){
		FacesContext context = FacesContext.getCurrentInstance();
		int scheduleid = Integer.parseInt(context.getExternalContext().getRequestParameterMap().get("scheduleId"));
		for(Schedule schedule:this.scheduleList){
			if(schedule.getId()==scheduleid){
				try {
					boolean isReadonly;
					if(schedule.getHost().equals(this.user.getUserInfo()) || schedule.isEditable()){
						isReadonly=false;
					} else {
						isReadonly = true;
					}
					this.initCalendar(schedule, isReadonly);
					break;
				} catch (DataBaseException e) {
					return "viewSchedule";
				}
			}
		}
		return "viewSchedule";
	}
	/**
	 * method used to serialize event data.
	 * @param schedule schedule event data belong to.
	 * @param isReadonly whether the schedule is read-only.
	 * @throws DataBaseException
	 */
	public void initCalendar(Schedule schedule, boolean isReadonly) throws DataBaseException{
		List<Event> eventList = this.eventService.getEvents(schedule);
		System.out.println("Get Schedule : "+eventList.size());
		this.selectSchedule = schedule;
		StringBuffer xml = new StringBuffer("<?xml version = '1.0'?>");
		xml.append("<data>");
		xml.append("<editable>"+isReadonly+"</editable>");
		for(Event event : eventList){
			System.out.println("Event StartTime:"+event.getStartTime().toString());
			xml.append("<event>");
			xml.append("<start_date>" + event.getStartTime().toString() + "</start_date>");
			xml.append("<end_date>" + event.getEndTime().toString() + "</end_date>");
			xml.append("<text>" + event.getDescription() + "</text>");
			xml.append("<id>" + event.getId() + "</id>");
			StringBuffer joinUserList = new StringBuffer("{");
			for(UserInfo user:event.getJoinedUsers()){
				joinUserList.append(user.getNickName()+";");
			}
			joinUserList.append("}");
			xml.append("<join_user>"+joinUserList.toString()+"</join_user>");
			xml.append("</event>");
		}
		xml.append("</data>");
		this.initXML = xml.toString();
		System.out.println("Schedule XML:"+xml.toString());
	}
	/**
	 * method used to respond to create schedule command.
	 * @return
	 */
	public String createSchedule(){
		Attribute attribute = new Attribute();
		attribute.put(ScheduleAttributeKind.editable, this.editable);
		attribute.put(ScheduleAttributeKind.publicAccess, this.public_access);
		attribute.put(ScheduleAttributeKind.clicks, 0);
		attribute.put(ScheduleAttributeKind.topic, this.topic);
		attribute.put(ScheduleAttributeKind.type, ScheduleType.GENERAL);
		String[] tag = this.tags.split(" ");
		List<String> tagList = Arrays.asList(tag);
		attribute.put(ScheduleAttributeKind.tags, tagList);
		try{
			Schedule newschedule = this.scheduleService.createSchedule(this.user, attribute);
			this.topic = null;
			this.editable = false;
			this.public_access = false;
			this.tags = null;
			if(newschedule!=null){
				this.scheduleList.add(newschedule);
				return "correct";
			}else return "failure";
		}catch(DataBaseException e){
			e.printStackTrace();
			return "failure";
		}
	}
	/**
	 * method used to respond to create event command for Ajax component.
	 * @param attribute the event attribute used to create event
	 * @return id of new event.
	 * @throws DataBaseException
	 */
	public int createEvent(Attribute attribute) throws DataBaseException{
		Event newEvent = this.eventService.createNewEvent(this.user, this.selectSchedule, attribute);
		return newEvent.getId();
	}
	/**
	 * method used to update a event.
	 * @param id id of target event
	 * @param attribute new attribute of target event
	 * @throws DataBaseException
	 */
	public void updateEvent(int id, Attribute attribute) throws DataBaseException{
		Event event = this.eventService.getEvent(id);
		event.setStartTime((DateWrapper) attribute.getValue(EventAttributeKind.START_TIME));
		event.setEndTime((DateWrapper) attribute.getValue(EventAttributeKind.END_TIME));
		event.setDescription((String) attribute.getValue(EventAttributeKind.DESCRIPTION));
		this.eventService.updateEvent(event);
	}
	/**
	 * method used to remove a event
	 * @param id id of target event
	 * @throws DataBaseException
	 */
	public void removeEvent(int id) throws DataBaseException{
		Event event = this.eventService.getEvent(id);
		this.eventService.removeEvent(this.selectSchedule, event);
	}
	/**
	 * method used to let the user join/unjoin a event
	 * @param id id of target event
	 * @return new serialize join user list.
	 * @throws DataBaseException
	 */
	public String interactEvent(int id) throws DataBaseException{
		Event event = this.eventService.getEvent(id);
		System.out.println(event);
		event.setType(EventType.INTERACTIVE);
		if(event.getJoinedUsers().contains(this.user.getUserInfo())){
			this.eventService.quitEvent(event, this.user);
			System.out.println("quit");
		} else {
			System.out.println("join:"+event.getJoinedUsers().size());
			this.eventService.joinEvent(event, this.user);
			System.out.println("join2:"+event.getJoinedUsers().size());

		}
		StringBuffer joinUserList = new StringBuffer("{");
		for(UserInfo user:event.getJoinedUsers()){
			joinUserList.append(user.getNickName()+";");
		}
		joinUserList.append("}");
		return joinUserList.toString();
	}
	/**
	 * method used to respond to delete schedule command
	 * @throws DataBaseException
	 */
	public void deleteSchedule() throws DataBaseException{
		List<Schedule> removeSchedule = new ArrayList<Schedule>();
		for(Schedule schedule:this.scheduleList){
			if(schedule.isSelected()){
				this.scheduleService.removeSchedule(schedule);
				removeSchedule.add(schedule);
			}
		}
		this.scheduleList.removeAll(removeSchedule);
	}
	/**
	 * method used to respond to merge schedule command.
	 * @throws DataBaseException
	 */
	public void mergeSchedule() throws DataBaseException{
		List<Schedule> mergeSchedules = new ArrayList<Schedule>();
		for(Schedule schedule:this.scheduleList){
			if(schedule.isSelected()){
				mergeSchedules.add(schedule);
			}
		}
		if(mergeSchedules.size()>=2){
			this.scheduleList.add(this.scheduleService.createSchedule(this.user, mergeSchedules));
		}
	}
	/**
	 * method used to respond to subscribe schedule command.
	 * @throws DataBaseException
	 */
	public void subscribeSchedule() throws DataBaseException{
		FacesContext context = FacesContext.getCurrentInstance();
		int scheduleid = Integer.parseInt(context.getExternalContext().getRequestParameterMap().get("scheduleId"));
		SearchBackingBean srbb = (SearchBackingBean)context.getExternalContext().getSessionMap().get("searchBackingBean");
		for(Schedule schedule:srbb.getSchedules()){
			if(schedule.getId()==scheduleid){
				if(this.getScheduleList().contains(schedule)){

				} else {
					this.scheduleService.addSubscribedSchedule(this.user, schedule);
					this.getScheduleList().add(schedule);
				}
				break;
			}
		}
	}
	/**
	 * method to respond to copy command for Ajax component
	 * @param eventId id of copy event
	 * @throws DataBaseException
	 */
	public void copyEvent (int eventId) throws DataBaseException{
		this.copiedEventId = eventId;
	}
	/**
	 * method to respond to paste event command for Ajax component
	 * @return serialize data of paste event
	 * @throws DataBaseException
	 */
	public String pasteEvent () throws DataBaseException{
		Event copiedEvent = this.eventService.getEvent(this.copiedEventId);
		if(copiedEvent != null){
			this.eventService.addEvent(selectSchedule, copiedEvent);
			StringBuffer xml = new StringBuffer();
			xml.append("<event>");
			xml.append("<start_date>" + copiedEvent.getStartTime().toString() + "</start_date>");
			xml.append("<end_date>" + copiedEvent.getEndTime().toString() + "</end_date>");
			xml.append("<text>" + copiedEvent.getDescription() + "</text>");
			xml.append("<id>" + copiedEvent.getId() + "</id>");
			StringBuffer joinUserList = new StringBuffer("{");
			for(UserInfo user:copiedEvent.getJoinedUsers()){
				joinUserList.append(user.getNickName()+";");
			}
			joinUserList.append("}");
			xml.append("<join_user>"+joinUserList.toString()+"</join_user>");
			xml.append("</event>");
			return xml.toString();
		} else return null;
	}
}
